
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.swing.JOptionPane;

/**
 *
 * @author Jeff
 */
public class IT_306_Group7 {

    public static void main(String[] args) {
        User user = null;
        int choice = 0, friendChoice = 0;
        choice = showMenu("What would you like to do:\n\n  1. Log In\n  2. New "
                + "User", 1, 2);
        if (choice == 2) {
            createUser();
            user = logIn();
        } else {
            user = logIn();
        }

        do {
            choice = showMenu("What would you like to do:\n\n  1. View Old "
                    + "Messages\n  2. Encrypt Messages\n  3. Decrypt Messages"
                    + "\n  4. Update Your Account\n  5. Add friend\n  6. "
                    + "View/Edit Friends\n  7. Exit", 1, 7);
            switch (choice) {
                case 1:
                    user.displayMessages();
                    break;
                case 2:
                    user = encryptMessages(user);
                    break;
                case 3:
                    user = decryptMessages(user);
                    break;
                case 4:
                    user = updateUser(user);
                    break;
                case 5:
                    user = addFriend(user);
                    break;
                case 6:
                    friendChoice = showMenu(user.displayFriend() + "Enter"
                            + " 0 to exit or enter your Friends ID to edit",
                            0, user.getFriendCount());
                    if (friendChoice != 0) {
                        user.updateFriend(modifyFriend(user.findFriend(
                                friendChoice)));
                    }
                    break;
                case 7:
                    JOptionPane.showMessageDialog(null, "Thanks for using "
                            + "Encryption Suite\nGoodbye");
                    break;
            }
        } while (choice != 7);


        // save file
        createDataFile(user);
    }

    public static User updateUser(User u) {
        int choice = 0;
        boolean isValidInput = false;
        do {
            choice = showMenu("What would you like to update:\n\n  1. Name"
                    + "\n  2. Password\n  3. Exit", 1, 3);

            switch (choice) {
                case 1:
                    isValidInput = false;
                    do {
                        isValidInput = u.setName(JOptionPane.showInputDialog(
                                "Enter your new name:"));
                        if (!isValidInput) {
                            JOptionPane.showMessageDialog(null, "That is not a "
                                    + "valid name");
                        }
                    } while (!isValidInput);
                    JOptionPane.showMessageDialog(null, "Updated information\n"
                            + u.toString());
                    break;
                case 2:
                    isValidInput = false;
                    do {
                        isValidInput = u.setPasswordHash(
                                JOptionPane.showInputDialog("Enter your new "
                                + "password:"));
                        if (!isValidInput) {
                            JOptionPane.showMessageDialog(null, "That is not a "
                                    + "valid password");
                        }
                    } while (!isValidInput);
                    JOptionPane.showMessageDialog(null, "Updated information\n"
                            + u.toString());
                    break;
                case 3:
                    JOptionPane.showMessageDialog(null, "Thank you for updating"
                            + " your information");
                    break;
            }
        } while (choice != 3);
        return u;
    }

    public static Friend modifyFriend(Friend friend) {
        int choice = 0;
        boolean isValidInput = false;
        do {
            choice = showMenu("Would you like to do for:\n\n" + friend.
                    toString() + "\n  1. "
                    + "Change your friends name\n  2. Add email\n  3. Delete "
                    + "email\n  4. Update Simplemonoalphabetic encryption key"
                    + "\n  5. Update Polyalphabetic encryption key\n  6. Update"
                    + " DES key\n  7. Update 3DES key\n  8.  Exit\n\n", 1, 8);
            switch (choice) {
                case 1:
                    do {
                        isValidInput = friend.setName(JOptionPane.
                                showInputDialog("Enter "
                                + "your friend's new name:"));
                        if (!isValidInput) {
                            JOptionPane.showMessageDialog(null, "That is not a"
                                    + " valid name");
                        }
                    } while (!isValidInput);
                    break;
                case 2:
                    do {
                        isValidInput = friend.addEMail(JOptionPane.
                                showInputDialog("Enter your friends email:"));
                        if (!isValidInput) {
                            JOptionPane.showMessageDialog(null, "That is not "
                                    + "a valid email");
                        }
                    } while (!isValidInput);
                    break;
                case 3:
                    isValidInput = friend.addEMail(JOptionPane.showInputDialog(
                            "Enter your friends email that you want to delete:"
                            + ""));
                    if (!isValidInput) {
                        JOptionPane.showMessageDialog(null, "That is not a valid "
                                + "email");
                    }
                    break;
                case 4:
                    friend = setKey(friend, 1);
                    break;
                case 5:
                    friend = setKey(friend, 2);
                    break;
                case 6:
                    friend = setKey(friend, 3);
                    break;
                case 7:
                    friend = setKey(friend, 4);
                    break;
                case 8:
                    break;
            }
        } while (choice != 8);
        return friend;
    }

    public static User encryptMessages(User u) {
        int choice = 0;
        Message message = null;
        choice = showMenu("Would you like to encrypt message to:\n\n  1. "
                + "Existing friend\n  2. Add new friend", 1, 2);
        if (choice == 2) {
            u = addFriend(u);
        }
        if (choice == 1 && u.getFriendCount() == 1) {
            JOptionPane.showMessageDialog(null, "Sorry you must add at least "
                    + "one friend");
            u = addFriend(u);
        }

        // select friend
        Friend friend = u.findFriend(showMenu(u.displayFriend() + "\nEnter your"
                + " friends ID", 1, u.getFriendCount()));
        choice = JOptionPane.showConfirmDialog(null, "Would you like "
                + "to update your friends info including add a key?", null,
                JOptionPane.YES_NO_OPTION);
        if (choice == JOptionPane.YES_OPTION) {
            friend = modifyFriend(friend);
        }

        // enter message
        String text = JOptionPane.showInputDialog(null, "Enter message:");

        // choose encryption type
        choice = showMenu("Select an encryption scheme:\n\n  1. Simple "
                + "monoalphabetic encryption\n  2. Polyalphabetic encryption"
                + "\n  3. DES\n  4. 3DES", 1, 4);
        switch (choice) {

            case 1:
                if (friend.getCeasarKey() == 0) {
                    friend = setKey(friend, 1);
                }
                // create message
                message = new CeasarShift(u, friend.getID(), text);
                ((CeasarShift) message).setCeasarKey(friend.getCeasarKey());
                if (message.encrypt()) {
                    JOptionPane.showMessageDialog(null, "Message encrypted "
                            + "successfully\nKey: " + friend.getCeasarKey()
                            + "\nMessage: " + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "encrypting your message");
                }
                break;
            case 2:
                if (friend.getSubKey() == 0) {
                    friend = setKey(friend, 2);
                }
                // create message
                message = new Substitution(u, friend.getID(), text);
                ((Substitution) message).setSubKey(friend.getSubKey());
                if (message.encrypt()) {
                    JOptionPane.showMessageDialog(null, "Message encrypted "
                            + "successfully\nKey: " + friend.getSubKey()
                            + "\nMessage: " + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "encrypting your message");
                }
                break;
            case 3:
                if (friend.getDESKey().equals("")) {
                    friend = setKey(friend, 3);
                }
                // create message
                message = new DES(u, friend.getID(), text);
                ((DES) message).setDESKey(friend.getDESKey());
                if (message.encrypt()) {
                    JOptionPane.showMessageDialog(null, "Message encrypted "
                            + "successfully\nKey: " + friend.getDESKey()
                            + "\nMessage: " + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "encrypting your message");
                }
                break;
            case 4:
                if (friend.getDESKey().equals("")) {
                    friend = setKey(friend, 3);
                }
                if (friend.getDESKey2().equals("")) {
                    friend = setKey(friend, 4);
                }
                // create message
                message = new TripleDES(u, friend.getID(), text);
                ((TripleDES) message).setDESKey(friend.getDESKey());
                ((TripleDES) message).setDESKey2(friend.getDESKey());
                if (message.encrypt()) {
                    JOptionPane.showMessageDialog(null, "Message encrypted "
                            + "successfully\nDES Key: " + friend.getDESKey()
                            + "\n3DES Key: " + friend.getDESKey2()
                            + "\nMessage: " + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "encrypting your message");
                }
                break;
        }
        u.updateFriend(friend);
        return u;
    }

//    Encryption types
//    1. Simplemonoalphabetic encryption
//    2. Polyalphabetic encryption
//    3. DES\
//    4. 3DES      
    public static Friend setKey(Friend friend, int encryptionType) {
        boolean isValidKey = false;
        int choice = 0;
        switch (encryptionType) {
            case 1:
                choice = showMenu("1. Enter a Simplemonoalphabetic encryption "
                        + "key\n2. Generate a Simplemonoalphabetic encryption "
                        + "key", 1, 2);
                if (choice == 1) {
                    do {
                        int key = 0;
                        String keyString = "";
                        if (isInteger(keyString = JOptionPane.showInputDialog(
                                "Enter your key"))) {
                            isValidKey = friend.setCeasarKey(Integer.parseInt(
                                    keyString));
                        }
                        if (!isValidKey) {
                            JOptionPane.showMessageDialog(null, "That is not a"
                                    + " valid key");
                        }
                    } while (!isValidKey);
                } else {
                    friend.setCeasarKey(CeasarShift.generateKey());
                }
                break;
            case 2:
                choice = showMenu("1. Enter a Polyalphabetic encryption "
                        + "key\n2. Generate a Polyalphabetic encryption "
                        + "key", 1, 2);
                if (choice == 1) {
                    do {
                        int key = 0;
                        String keyString = "";
                        if (isInteger(keyString = JOptionPane.showInputDialog(
                                "Enter your key"))) {
                            isValidKey = friend.setSubKey(Integer.parseInt(
                                    keyString));
                        }
                        if (!isValidKey) {
                            JOptionPane.showMessageDialog(null, "That is not a"
                                    + " valid key");
                        }
                    } while (!isValidKey);
                } else {
                    friend.setSubKey(Substitution.generateKey());
                }
                break;
            case 3:
                choice = showMenu("1. Enter a DES encryption "
                        + "key\n2. Generate a DES encryption "
                        + "key", 1, 2);
                if (choice == 1) {
                    do {
                        String keyString = "";
                        keyString = JOptionPane.showInputDialog("Enter your "
                                + "key");
                        if (!keyString.equals("") || keyString != null) {
                            isValidKey = friend.setDESKey(keyString);
                        }
                        if (!isValidKey) {
                            JOptionPane.showMessageDialog(null, "That is not a"
                                    + " valid key");
                        }
                    } while (!isValidKey);
                } else {
                    friend.setDESKey(DES.generateKey());
                }
                break;
            case 4:
                choice = showMenu("1. Enter a 3DES encryption "
                        + "key\n2. Generate a 3DES encryption "
                        + "key", 1, 2);
                if (choice == 1) {
                    do {
                        String keyString = "";
                        keyString = JOptionPane.showInputDialog("Enter your"
                                + " key");
                        if (!keyString.equals("") || keyString != null) {
                            isValidKey = friend.setDESKey2(keyString);
                        }
                        if (!isValidKey) {
                            JOptionPane.showMessageDialog(null, "That is not a"
                                    + " valid key");
                        }
                    } while (!isValidKey);
                } else {
                    friend.setDESKey2(TripleDES.generateKey());
                }
                break;
        }

        return friend;
    }

    public static User decryptMessages(User u) {
        int choice = 0;
        Message message = null;

        choice = showMenu("Would you like to decrypt message from:\n\n  1. "
                + "Existing friend\n  2. Add new friend", 1, 2);
        if (choice == 2) {
            u = addFriend(u);
        }
        if (choice == 1 && u.getFriendCount() == 1) {
            JOptionPane.showMessageDialog(null, "Sorry you must add at least "
                    + "one friend");
            u = addFriend(u);
        }

        // select friend
        Friend friend = u.findFriend(showMenu(u.displayFriend() + "\nEnter your"
                + " friends ID", 1, u.getFriendCount()));
        choice = JOptionPane.showConfirmDialog(null, "Would you like "
                + "to update your friends info including add a key?", null,
                JOptionPane.YES_NO_OPTION);
        if (choice == JOptionPane.YES_OPTION) {
            friend = modifyFriend(friend);
        }

        // enter message
        String text = JOptionPane.showInputDialog(null, "Enter message:");


        // choose encryption type
        choice = showMenu("Select an decryption scheme:\n\n  1. Simple "
                + "monoalphabetic encryption\n  2. Polyalphabetic encryption"
                + "\n  3. DES\n  4. 3DES", 1, 4);
        switch (choice) {

            case 1:
                if (friend.getCeasarKey() == 0) {
                    friend = setKey(friend, 1);
                }
                // create message
                message = new CeasarShift(u, friend.getID(), text);
                ((CeasarShift) message).setCeasarKey(friend.getCeasarKey());
                if (message.decrypt()) {
                    JOptionPane.showMessageDialog(null, "Message decrypted "
                            + "successfully\n\n" + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "decrypting your message");
                }
                break;
            case 2:
                if (friend.getSubKey() == 0) {
                    friend = setKey(friend, 2);
                }
                // create message
                message = new Substitution(u, friend.getID(), text);
                ((Substitution) message).setSubKey(friend.getSubKey());
                if (message.decrypt()) {
                    JOptionPane.showMessageDialog(null, "Message decrypted "
                            + "successfully\n\n" + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "decrypting your message");
                }
                break;
            case 3:
                if (friend.getDESKey().equals("")) {
                    friend = setKey(friend, 3);
                }
                // create message
                message = new DES(u, friend.getID(), text);
                ((DES) message).setDESKey(friend.getDESKey());
                if (message.decrypt()) {
                    JOptionPane.showMessageDialog(null, "Message decrypted "
                            + "successfully\nKey: " + friend.getDESKey()
                            + "\n" + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "decrypting your message");
                }
                break;
            case 4:
                if (friend.getDESKey().equals("")) {
                    friend = setKey(friend, 3);
                }
                if (friend.getDESKey2().equals("")) {
                    friend = setKey(friend, 4);
                }
                // create message
                message = new TripleDES(u, friend.getID(), text);
                ((TripleDES) message).setDESKey(friend.getDESKey());
                ((TripleDES) message).setDESKey2(friend.getDESKey());
                if (message.decrypt()) {
                    JOptionPane.showMessageDialog(null, "Message decrypted "
                            + "successfully\nDES Key: " + friend.getDESKey()
                            + "\n3DES Key: " + friend.getDESKey2()
                            + "\n" + message.getText());
                } else {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "decrypting your message");
                }
                break;
        }
        u.updateFriend(friend);
        return u;
    }

    public static User addFriend(User u) {
        Friend friend = new Friend(u.getFriendCount());
        boolean isValidInput = false;
        int moreEmail = JOptionPane.NO_OPTION;

        do {
            isValidInput = friend.setName(JOptionPane.showInputDialog("Enter "
                    + "your friend's name:"));
            if (!isValidInput) {
                JOptionPane.showMessageDialog(null, "That is not a valid name");
            }
        } while (!isValidInput);
        isValidInput = false;
        do {
            do {
                isValidInput = friend.addEMail(JOptionPane.showInputDialog(
                        "Enter your friends email:"));
                if (!isValidInput) {
                    JOptionPane.showMessageDialog(null, "That is not a valid "
                            + "email");
                }
            } while (!isValidInput);
            moreEmail = JOptionPane.showConfirmDialog(null, "Would you like "
                    + "to add another email?", null, JOptionPane.YES_NO_OPTION);
        } while (moreEmail == JOptionPane.YES_OPTION);
        // add keys
        u.addFriend(friend);
        JOptionPane.showMessageDialog(null, "Friend Added Successfully\n"
                + friend.toString());
        return u;
    }

    public static User logIn() {
        User user = null;
        int choice = 0;
        boolean validLogIn = false, validID = false;
        String iD = "", password = "";
        do {

            iD = JOptionPane.showInputDialog(null, "Please enter your user ID",
                    "Login", JOptionPane.QUESTION_MESSAGE);
            password = JOptionPane.showInputDialog(null, "Please enter your "
                    + "password", "Login", JOptionPane.QUESTION_MESSAGE);

            if (isValidLogin(iD, password)) {
                user = readDataFile(iD, password);
                validLogIn = true;
            } else {
                choice = showMenu("Not a valid login\nWhat would you like to "
                        + "do:\n\n  1. Try to Login again\n  2. Create New "
                        + "User", 1, 2);
                if (choice == 2) {
                    createUser();
                }
            }
        } while (!validLogIn);
        return user;
    }

    public static void createUser() {
        User user = new User(userCount());
        boolean isValidInput = false;
        do {
            isValidInput = user.setName(JOptionPane.showInputDialog("Enter your"
                    + " name:"));
            if (!isValidInput) {
                JOptionPane.showMessageDialog(null, "That is not a valid name");
            }
        } while (!isValidInput);
        isValidInput = false;
        do {
            isValidInput = user.setPasswordHash(JOptionPane.showInputDialog(
                    "Enter your password:"));
            if (!isValidInput) {
                JOptionPane.showMessageDialog(null, "That is not a valid "
                        + "password");
            }
        } while (!isValidInput);
        JOptionPane.showMessageDialog(null, user.toString());
        createDataFile(user);

    }

    public static int userCount() {
        File userCountFile = new File("userCount");
        Object obj = null;
        int count = 0;

        // create file if doesn't exist and set count to 0
        if (!userCountFile.exists()) {
            try {
                FileOutputStream countFile =
                        new FileOutputStream(userCountFile);
                ObjectOutputStream outCount = new ObjectOutputStream(countFile);
                outCount.writeObject(count);
                outCount.close(); // This also closes saveFile.
            } catch (Exception exc) {
                JOptionPane.showMessageDialog(null, "There was an error "
                        + "Creating a File\nExit the program and delete "
                        + "userCount file and all User files and try again");
            }
        } // read user count
        else {
            try {
                FileInputStream iCountFile = new FileInputStream(userCountFile);
                ObjectInputStream iCount = new ObjectInputStream(iCountFile);
                obj = iCount.readObject();
                if (obj instanceof Integer) {
                    count = (Integer) obj;
                }
                iCount.close();

                // update user count
                try {
                    FileOutputStream countFile =
                            new FileOutputStream(userCountFile);
                    ObjectOutputStream outCount =
                            new ObjectOutputStream(countFile);
                    outCount.writeObject(count += 1);
                    outCount.close(); // This also closes saveFile.
                } catch (Exception exc) {
                    JOptionPane.showMessageDialog(null, "There was an error "
                            + "Creating a File\nExit the program and delete "
                            + "userCount file and all User files and try again");
                }
            } // add different exceptions and appropriate error messages
            catch (IOException | ClassNotFoundException exc) {
                JOptionPane.showMessageDialog(null, "There was an error "
                        + "Creating a File\nExit the program and delete "
                        + "userCount file and all User files and try again");
            }
        }
        return count;
    }

    public static void createDataFile(User user) {
        File userFile = new File(String.valueOf(user.getID()));


        try {
            FileOutputStream saveFile = new FileOutputStream(userFile);
            ObjectOutputStream save = new ObjectOutputStream(saveFile);
            save.writeObject(String.valueOf(user.getID()));
            save.writeObject(user.getPasswordHash());
            save.writeObject(user);
            save.close();
        } catch (Exception exc) {
            JOptionPane.showMessageDialog(null, "There was an error "
                    + "Creating a File\nExit the program and delete "
                    + "userCount file and all User files and try again");
        }
    }

    public static boolean isValidLogin(String iID, String iPassword) {
        File userFile = new File(iID);
        String iD = "", password = "";
        if (userFile.exists()) {
            try {
                FileInputStream saveFile = new FileInputStream(userFile);
                ObjectInputStream save = new ObjectInputStream(saveFile);

                Object obj = save.readObject();
                if (obj instanceof String) {
                    iD = (String) obj;
                }

                obj = save.readObject();
                if (obj instanceof String) {
                    password = (String) obj;
                }
                save.close();
            } catch (IOException | ClassNotFoundException exc) {
                JOptionPane.showMessageDialog(null, "There was an error "
                        + "Creating a File\nExit the program and delete "
                        + "userCount file and all User files and try again");
            }
            if (iD.equals(iID) && password.equals(
                    User.hashPassword(iPassword))) {
                return true;
            }
        }
        return false;
    }

    public static User readDataFile(String iID, String iPassword) {
        User user = null;
        Object iD = null, password = null, obj = null;;
        File userFile = new File(String.valueOf(iID));

        try {
            FileInputStream saveFile = new FileInputStream(userFile);
            ObjectInputStream save = new ObjectInputStream(saveFile);
            iD = save.readObject();
            password = save.readObject();
            obj = save.readObject();
            if (obj instanceof User) {
                user = (User) obj;
            }
            save.close();
        } catch (IOException | ClassNotFoundException exc) {
            JOptionPane.showMessageDialog(null, "There was an error "
                    + "Creating a File\nExit the program and delete "
                    + "userCount file and all User files and try again");
            
        }
        return user;
    }

    public static int showMenu(String menu, int firstNum, int lastNum) {
        String menuChoice = "";
        boolean validInt = false;
        boolean validRange = false;
        do {
            menuChoice = JOptionPane.showInputDialog(menu);
            validInt = isInteger(menuChoice);
            if (validInt) {
                validRange = isValidRange(Integer.parseInt(menuChoice),
                        firstNum, lastNum);
            }
        } while (!validInt || !validRange);
        return Integer.parseInt(menuChoice);
    }

    public static boolean isValidRange(int selection, int firstNum,
            int lastNum) {
        if (selection < firstNum || selection > lastNum) {
            JOptionPane.showMessageDialog(null, "That is not a valid "
                    + "selection");
            return false;
        } else {
            return true;
        }
    }

    public static boolean isInteger(String input) {
        try {
            Integer.parseInt(input);
            return true;
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(null, "That is not a valid integer");
            return false;
        }
    }
}
